package utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import nanoxml.XMLElement;

/**
 * The abstract class for all connection threads. It listens to the inputs stream of the connection and listens for a message.
 * Once it gets one, it sends it to the {@link #parse(XMLElement)} method to parse it and act according to the specific command.
 */
public abstract class AbstractConnectionThread extends Thread {

	private static final String XML_PREFIX = "<";
	
	private static final int TIMEOUT_FOR_READ = 1000;
	
	// This message is sent periodically to check if the socket is still open.
	// It is not caught on the other side and not handled in any way.
	private static final String MESSAGE_TO_CHECK_SOCKET = "HuchiMama";
	
	private BufferedReader in;
	private PrintStream out;
	private boolean loop = true;

	protected Socket socket;
	protected String username;
	protected String password;
	
	
	public AbstractConnectionThread (Socket socket) {
		loop = true;
		
		try {
			setSocket (socket);
		}
		catch (IOException ioEx) {
			ioEx.printStackTrace();
		}
	}
	
	public Socket getSocket()
	{
		return socket;
	}
	
	protected void setSocket (Socket socket) throws IOException {
	    this.socket = socket;

	    if (socket != null) {
		    in = new BufferedReader (new InputStreamReader (socket.getInputStream(),"UTF-8"));
		    out = new PrintStream (socket.getOutputStream());
		}
	}
	
	/**
	 * The run method for this Thread. It keeps getting lines of input from the input stream, and then parse them to take the necessary action.
	 */
	public void run () {
		try {
			while (loop) {
				// Get the current line from the input stream. This method blocks until a line of input arrives
				socket.setSoTimeout(TIMEOUT_FOR_READ);
				
				try {
					String inString = in.readLine();
					
					if (inString != null) {
						if (inString.startsWith(XML_PREFIX)) {
							// Starts with an '<' so try and parse this XML
							XMLElement message = new XMLElement();
							message.parseString(inString);   // parse this element
							if (message != null)
								parse(message);	
						}
					}
					else
					{
						loop = false;
						socket.close();
					}
				}
				catch (SocketTimeoutException e) {
					send(MESSAGE_TO_CHECK_SOCKET);
				}
			}
		}
		catch (SocketException e)
		{
			if (loop)
			{
				handleDisconnection();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (Exception genEx) {
			genEx.printStackTrace();		
		}	
	}

	public void stopLoop () {
		this.loop = false;
	}

	public void send(XMLElement command){
		send(command.toString());
	}
	
	public void send(ITransmittable element) {
		send(element.toXML().toString());
	}

	public void send(String message)
	{
		out.println(message);
	}
	
	public abstract void parse(XMLElement message); 
	public abstract void handleDisconnection(); 
	
	public String getIP() {
		return socket.getLocalAddress().toString();
	}
	
	/**
	 * Gets the external IP of the computer, meaning not the IP given to the computer by the router of the LAN it is in, but the IP
	 * that was assigned to the router to communicate with the rest of the Internet.
	 *  
	 * @return The external IP.
	 */
	public String getExternalIP() {
		
		try {
			java.net.URL URL = new java.net.URL("http://whatismyip.com/automation/n09230945.asp");
			java.net.HttpURLConnection Conn = (HttpURLConnection)URL.openConnection();
			java.io.InputStream InStream = Conn.getInputStream();
			java.io.InputStreamReader Isr = new java.io.InputStreamReader(InStream);
			java.io.BufferedReader br = new java.io.BufferedReader(Isr);
			return br.readLine();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
